package xin.i18n.resolver;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.i18n.LocaleContext;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.i18n.SimpleLocaleContext;
import org.springframework.context.i18n.TimeZoneAwareLocaleContext;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.LocaleContextResolver;
import xin.i18n.util.LocaleType;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;

/**
 * {@link LocaleContextResolver} 的实现
 * 使用HTTP请求中包含的请求头“lang”指定的主语言环境即，由客户端浏览器发送的语言环境，由客户端主动选择).
 *
 * <p>注意：不支持setLocale，因为只能通过更改客户端的区域设置来更改lang头
 *
 * @author lixin_ma@outlook.com
 * @version 1.0
 * @since 2019/11/26
 */
public class LangHeaderLocaleResolver implements LocaleContextResolver {
	protected final Log logger = LogFactory.getLog(this.getClass());
	private final List<Locale> supportedLocales = new ArrayList<>(4);
	
	private String headerName;
	
	@Nullable
	private Locale defaultLocale;
	
	public void setSupportedLocales(List<Locale> locales) {
		this.supportedLocales.clear();
		this.supportedLocales.addAll(locales);
	}
	
	public LangHeaderLocaleResolver() {
	}
	
	
	public List<Locale> getSupportedLocales() {
		return this.supportedLocales;
	}
	
	public void setDefaultLocale(@Nullable Locale defaultLocale) {
		this.defaultLocale = defaultLocale;
	}
	
	@Nullable
	public String getHeaderName() {
		return this.headerName;
	}
	
	public void setHeaderName(@Nullable String headerName) {
		this.headerName = headerName;
	}
	
	@Nullable
	public Locale getDefaultLocale() {
		return this.defaultLocale;
	}
	
	
	@Override
	public Locale resolveLocale(HttpServletRequest request) {
		String requestLang = request.getHeader(headerName);
		Locale locale;
		if (requestLang != null && !"".equals(requestLang)) {
			locale = LocaleType.getValue(requestLang);
		} else {
			locale = getDefaultLocale();
		}
		if (isSupportedLocale(locale)) {
			return locale;
		} else {
			return getDefaultLocale();
		}
	}
	
	private boolean isSupportedLocale(Locale locale) {
		List<Locale> supportedLocales = getSupportedLocales();
		return (supportedLocales.isEmpty() || supportedLocales.contains(locale));
	}
	
	@Override
	public void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale) {
		setLocaleContext(request, response, (locale != null ? new SimpleLocaleContext(locale) : null));
	}
	
	@Override
	public LocaleContext resolveLocaleContext(HttpServletRequest request) {
		return new TimeZoneAwareLocaleContext() {
			@Override
			@Nullable
			public Locale getLocale() {
				return resolveLocale(request);
			}
			
			@Override
			public TimeZone getTimeZone() {
				return getDefaultTimeZone();
			}
		};
	}
	
	@Override
	public void setLocaleContext(HttpServletRequest request, HttpServletResponse response, LocaleContext localeContext) {
		LocaleContextHolder.setLocaleContext(localeContext == null ? new SimpleLocaleContext(resolveLocale(request)) : localeContext, false);
	}
	
	@Nullable
	private TimeZone defaultTimeZone;
	
	public void setDefaultTimeZone(@Nullable TimeZone defaultTimeZone) {
		this.defaultTimeZone = defaultTimeZone;
	}
	
	@Nullable
	public TimeZone getDefaultTimeZone() {
		return this.defaultTimeZone;
	}
	
}
